﻿using RockBus.DataContracts;
using RockBus.Logging.Interfaces;
using RockBus.Routing.Interfaces;
using RockBus.Transport.Interfaces;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Reflection;

namespace RockBus.Routing.SendPorts
{
    public class TransportManager : ITransportManager
    {
        private TransportManager(ILogger logger)
        {
            this.TransportFactories = new Dictionary<string, ITransportFactory>();
            this.Logger = logger;
        }

        public TransportManager(ILogger logger, ITransportFactory[] transportFactories)
            : this(logger)
        {
            foreach (ITransportFactory tf in transportFactories)
            {
                this.TransportFactories.Add(tf.TransportName, tf);
            }
        }

        public ILogger Logger { get; set; }

        private IDictionary<string, ITransportFactory> TransportFactories { get; set; }

        private readonly Dictionary<EndpointInfo, ITransport> Transports = new Dictionary<EndpointInfo, ITransport>(new EndpointInfoComparer());

        private readonly object _transportsLockObject = new object();

        public ITransport GetOrCreateClient(EndpointInfo key)
        {
            this.Logger.LogDebug(MethodBase.GetCurrentMethod(), "For Endpoint {0}", key.Address);

            try
            {
                ITransport transport;
                if (!this.Transports.TryGetValue(key, out transport))
                {
                    if (!this.TransportFactories.ContainsKey(key.TransportName))
                    {
                        throw new ConfigurationErrorsException(string.Format("Could not find TransportFactory for transport {0}", key.TransportName));
                    }

                    lock (_transportsLockObject)
                    {
                        if (!this.Transports.TryGetValue(key, out transport))
                        {
                            transport = this.TransportFactories[key.TransportName].CreateTransport(key);
                        }
                    }
                }

                return transport;
            }
            catch (Exception exc)
            {
                this.Logger.Log(MethodBase.GetCurrentMethod(), exc);
                throw;
            }
        }
    }
}